(* Copyright (c) 2013-2024 by Kithara Software GmbH. All rights reserved. *)

//##############################################################################################################
//
// File:         NetworkUdpSend.dpr
//
// Used Modules: Network Module
//
// Description:  Sample application showing how to implement a UDP sender
//
// Creator:      r.gro 2013-01-15
//
//##############################################################################################################

   {*=====================================================================*\
   |                    *** DISCLAIMER OF WARRANTY ***                     |
   |                                                                       |
   |       THIS  CODE AND INFORMATION IS PROVIDED 'AS IS'  WITHOUT         |
   |       A  WARRANTY OF  ANY KIND, EITHER  EXPRESSED OR  IMPLIED,        |
   |       INCLUDING  BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF         |
   |       MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.        |
   \*=====================================================================*}

//##############################################################################################################
//
// Purpose:
//
// This example shows how to send UDP datagrams.
//
// First we open the network adapter and create a socket.
// Then we send incoming keyboard characters to a target socket.
//
// This sample is designed to be the counterpart to the sample NetworkUdpReceive.
//
// PLEASE SPECIFY YOUR IP-ADDRESS, SUBNET-MASK AND GATEWAY-IP-ADDRESS IN THE KS_makeIPv4() CALLS AS WELL AS
// THE RECEIVER IP-ADRESS AND PORT IN THE APPLICATION PROGRAMM BEFORE RUNNING THIS EXAMPLE!
//
//##############################################################################################################

program NetworkUdpSend;

{$APPTYPE CONSOLE}

uses
  _KitharaSmp in '..\_KitharaSmp\_KitharaSmp.pas',
  KrtsDemo in '..\..\dev\KrtsDemo.pas',
  NetworkUdpSend_inc in 'NetworkUdpSend_inc.pas';


//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Don't forget to enter your zero-leading, 6-digit customer number!
// This is required to open the driver.
//
// If you use a Demo Version, 'DEMO' is also allowed instead.
// If you use a Beta Version, 'BETA' is also allowed instead.
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

const
  _pCustomerNumber : PAnsiChar = 'DEMO';


//--------------------------------------------------------------------------------------------------------------
// This is the main program.
//--------------------------------------------------------------------------------------------------------------

procedure runSample();
var
  error         : KSError;
  hSharedMemory : KSHandle;
  pApp          : PSharedData;
  systemInfo    : KSSystemInformation;
  pKernelName   : PAnsiChar;
  pDeviceName   : array[0..255] of AnsiChar;
  i             : Integer;
  deviceIndex   : Integer;
  sendText      : string;

begin
  outputTxt('***** Kithara example program ''NetworkUdpSend'' *****');


  //------------------------------------------------------------------------------------------------------------
  // Opening the driver is always the first step!
  // After that, we can use other functions. If the opening fails, no other function can be called.
  //
  // This function takes your customer number with Kithara (or "DEMO" or "BETA" if applicable) as a parameter.
  //------------------------------------------------------------------------------------------------------------

  error := KS_openDriver(
             _pCustomerNumber);                         // Customer number
  if error <> KS_OK then
  begin
    outputErr(error, 'KS_openDriver', 'Unable to open the driver!');
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // Allocate shared memory for communication between the DLL that gets loaded to realtime context and this
  // user space application.
  //------------------------------------------------------------------------------------------------------------

  error := KS_createSharedMemEx(
             @hSharedMemory,                            // Address to write the new shared memory handle to
             '',                                        // Name of shared memory
             sizeof(SharedData),                        // Size of shared memory
             KSF_NO_FLAGS);                             // Flags, here none
  if error <> KS_OK then
  begin
    outputErr(error, 'KS_createSharedMemEx', 'Unable to create shared memory!');
    KS_closeDriver();
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // To access the shared memory the application needs to acquire a pointer to the allocated shared memory
  // using the handle of the just created shared memory.
  //------------------------------------------------------------------------------------------------------------

  error := KS_getSharedMemEx(
             hSharedMemory,                             // Handle of the shared memory
             @pApp,                                     // Address of pointer to SharedData record
             KSF_NO_FLAGS);                             // Flags, here none
  if error <> KS_OK then
  begin
    outputErr(error, 'KS_getSharedMemEx', 'Unable to map shared memory!');
    KS_closeDriver();
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // Determine the system's bitsize to decide if either to load the 32 bit or 64 bit kernel DLL.
  //------------------------------------------------------------------------------------------------------------

  systemInfo.structSize := sizeof(KSSystemInformation); // Don't forget to init structSize!
  error := KS_getSystemInformation(
             @systemInfo,                               // Address of KSSystemInformation record
             KSF_NO_FLAGS);                             // Flags (unused)
  if error <> KS_OK then
  begin
    outputErr(error, 'KS_getSystemInformation', 'Unable to get system information to distinguish bitsize !');
    KS_closeDriver();
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // In order to use functions from the DLL on the kernel level, it must have been loaded first.
  //
  // Attention! The DLL must be found by the loader, so it should be placed in a directory, which is part
  // of the search path!
  //
  // Because we want to pass the handle of the loaded kernel in shared memory we don't use the
  // init routine on loading the kernel but will call the init routine explicitly later, after filling
  // the kernel handle and all information needed to initialize the kernel into the SharedData record.
  //------------------------------------------------------------------------------------------------------------

  if systemInfo.isSys64Bit = 0 then
    pKernelName := 'NetworkUdpSend_32.dll'
  else
    pKernelName := 'NetworkUdpSend_64.dll';
  error := KS_loadKernel(
             @pApp.hKernel,                             // Address to write kernel handle to
             pKernelName,                               // Name of DLL
             nil,                                       // Name of init routine (not used)
             nil,                                       // Init parameter (not used)
             KSF_KERNEL_EXEC);                          // Flags, here load to kernel address space
  if error <> KS_OK then
  begin
    outputErr(error, 'KS_loadKernel', 'Unable to load DLL! Is the DLL in the search path?');
    KS_closeDriver();
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // First, the names of all network adapters are queried and displayed.
  //------------------------------------------------------------------------------------------------------------

  outputTxt(' ');
  outputTxt('Following network adapters found:');

  i := 0;
  while true do
  begin


    //----------------------------------------------------------------------------------------------------------
    // KS_enumDevices() can be used to query the names of all network adapters assigned to the Kithara
    // Driver. The number "i" runs starting at zero.
    //----------------------------------------------------------------------------------------------------------

    error := KS_enumDevices(
               'NET',                                   // "NET" searches for network adapters
               i,                                       // Enumeration index, starting at zero
               pDeviceName,                             // Buffer for device name
               KSF_NO_FLAGS);                           // Flags, here none
    if error <> KS_OK then
    begin
      if KSERROR_CODE(error) <> KSERROR_DEVICE_NOT_FOUND then
        outputErr(error, 'KS_enumDevices', 'Unable to query network device name!');
      if (KSERROR_CODE(error) = KSERROR_DEVICE_NOT_FOUND) and (i = 0) then
      begin
        outputTxt('No network adapters found');
        outputTxt(' ');
        KS_closeDriver();
        exit;
      end;
      break;
    end;
    outputDec(i, '', ': ', false);
    outputTxt(pDeviceName);
    Inc(i);
  end;
  outputTxt(' ');


  //------------------------------------------------------------------------------------------------------------
  // Please enter the index of the adapter, which should be opened.
  //------------------------------------------------------------------------------------------------------------

  outputTxt('Attention!');
  outputTxt('By selecting a device its Windows driver gets removed and replaced by the ');
  outputTxt('appropriate Kithara driver. This will render the network device for the duration');
  outputTxt('of this sample invisible to Windows.');
  outputTxt('Be sure that all other Applications using that device are closed right now!');

  deviceIndex := inputDec('Device number: ', 0);

  error := KS_enumDevices(
             'NET',                                     // "NET" searches for network devices
             deviceIndex,                               // Index of the chosen network device
             pApp.pDeviceName,                          // Buffer for device name
             KSF_NO_FLAGS);                             // Flags, here none
  if error <> KS_OK then
  begin
    outputErr(error, 'KS_enumDevices', 'Unable to query selected network device name!');
    KS_closeDriver();
    exit;
  end;

  outputTxt('Selected device: ', false);
  outputTxt(pApp.pDeviceName);


  //------------------------------------------------------------------------------------------------------------
  // For sending IP packets we must set our own IP address and the subnet mask.
  // Note: The data in Ethernet frames must be "big endian"!
  // KS_htonl() converts an uint from host to net.
  // KS_ntohl() converts an uint from net to host.
  // KS_htons() converts an ushort from host to net.
  // KS_ntohs() converts an ushort from net to host.
  // KS_makeIPv4() builds an IP address.
  //------------------------------------------------------------------------------------------------------------


  KS_makeIPv4(@pApp.ipConfig.localAddress,   192, 168,   0, 182);
  KS_makeIPv4(@pApp.ipConfig.subnetMask,     255, 255, 255,   0);
  KS_makeIPv4(@pApp.ipConfig.gatewayAddress, 192, 168,   0,   1);
  KS_makeIPv4(@pApp.serverIp,                192, 168,   0, 181);
  pApp.udpPort := 2120;

  outputTxt('Current local IP address:       ', false);
  outputIpv4(KS_ntohl(pApp.ipConfig.localAddress));
  outputTxt('Current remote IP address:      ', false);
  outputIpv4(KS_ntohl(pApp.serverIp));
  outputTxt('Using port:                     ', false);
  outputDec(pApp.udpPort);
  outputTxt('Otherwise change the example!');
  outputTxt(' ');


  //------------------------------------------------------------------------------------------------------------
  // Now call the init routine of the kernel and pass the handle of the shared memory, so the kernel
  // can retrieve the pointer to the shared memory from the handle and do all necessary resource
  // allocations based on the information stored in the shared memory.
  //------------------------------------------------------------------------------------------------------------

  error := KS_execKernelFunctionEx(
             pApp.hKernel,                              // Kernel handle
             '_initFunction',                           // Name of function
             hSharedMemory,                             // Handle of the shared memory
             KS_INVALID_HANDLE,                         // Context (unused)
             KSF_NO_FLAGS);                             // Flags (unused)
  if error <> KS_OK then
  begin
    outputErr(error, 'KS_execKernelFunctionEx', 'Unable to initialize the kernel DLL!');
    KS_closeDriver();
    exit;
  end;

  outputTxt('Connected...');
  outputTxt(' ');


  //------------------------------------------------------------------------------------------------------------
  // Now, text messages can be entered and sent to the receiver.
  // Enter "Q" to quit the sample.
  //------------------------------------------------------------------------------------------------------------

  outputTxt('press <q> to quit');

  while true do begin

    sendText := inputTxt();
    if (sendText = 'q') or (sendText = 'Q') then
      break;

    StrCopy(pApp.pSendBuffer, PAnsiChar(AnsiString(sendText)));
    pApp.sendLength := length(sendText);
    error := KS_execKernelFunctionEx(
               pApp.hKernel,                            // Kernel handle
               '_sendBufferContent',                    // Name of function
               KS_INVALID_HANDLE,                       // Parameter for function call (unused)
               KS_INVALID_HANDLE,                       // Context (unused)
               KSF_NO_FLAGS);                           // Flags (unused)
    if error <> KS_OK then
    begin
       outputErr(error, '_sendBufferContent', 'Error while sending!');
       break;
    end;
  end;
  outputTxt(' ');


  //------------------------------------------------------------------------------------------------------------
  // Now let the DLL clean up resources it did allocate.
  //------------------------------------------------------------------------------------------------------------

  error := KS_execKernelFunctionEx(
             pApp.hKernel,                              // Kernel handle
             '_exitFunction',                           // Name of function
             KS_INVALID_HANDLE,                         // Parameter for function call (unused)
             KS_INVALID_HANDLE,                         // Context (unused)
             KSF_NO_FLAGS);                             // Flags (unused)
  if error <> KS_OK then
  begin
    outputErr(error, 'KS_execKernelFunctionEx', 'Error while deallocating resources on kernel level!');
    KS_closeDriver();
    exit;
  end;


  //------------------------------------------------------------------------------------------------------------
  // Unload the kernel DLL using the shared handle.
  // Although KS_closeDriver() frees all allocated resources (like shared memory and loaded kernels),
  // it is good style to explicitly free ahead resources that you did allocate.
  //------------------------------------------------------------------------------------------------------------

  error := KS_freeKernel(
             pApp.hKernel);                             // Kernel handle
  if error <> KS_OK then
    outputErr(error, 'KS_freeKernel', 'Unable to unload the kernel!');


  //------------------------------------------------------------------------------------------------------------
  // Free the shared memory using its local stored handle.
  //------------------------------------------------------------------------------------------------------------

  error := KS_freeSharedMemEx(
             hSharedMemory,                             // Handle of shared memory
             KSF_NO_FLAGS);                             // Flags (unused)
  if error <> KS_OK then
    outputErr(error, 'KS_freeSharedMemEx', 'Unable to remove shared memory!');


  //------------------------------------------------------------------------------------------------------------
  // At last we have to close the driver to free any allocated resources.
  //------------------------------------------------------------------------------------------------------------

  error := KS_closeDriver();
  if error <> KS_OK then
    outputErr(error, 'KS_closeDriver', 'Unable to close the driver!');

  waitTime(500 * ms);
  outputTxt(' ');
  outputTxt('End of program ''NetworkUdpSend''.');
end;


//--------------------------------------------------------------------------------------------------------------
// Entry point of the sample.
//--------------------------------------------------------------------------------------------------------------

begin
  runSample();
  outputTxt('Press <enter> to close.');
  Readln;
end.
